Micron Document
Fox's Git Mirrors

Commit 9607a1e53bf8b1f4527ecc99989a960ab8aa5a11


Parents : a757555
Author : zenith <157907903+RFnexus@users.noreply.github.com>
Date : 2026-07-07T23:39:10-04:00

Updates for modem73 v2.0.0. Compute bitrate, add option to add 2x link packets, automatically frame packets that are lower than the payload size and fit into short packets

Changes

2 files changed, 184 insertions(+), 0 deletions(-)

M README.md +6

Diff

diff --git a/Modem73Interface.py b/Modem73Interface.py
index 5a7d3dc..ab38cf1 100644
--- a/Modem73Interface.py
+++ b/Modem73Interface.py
@@ -14,7 +14,11 @@
# # Optional, defaults shown:
# # mtu_overhead = 15
# # bitrate = 600
+# # short_frames = off # off | auto | always
+# # short_mtu = 170
+# # handshake_x2 = no # send link handshake packets twice
+import base64
import json
import socket
import struct
@@ -33,10 +37,27 @@ class Modem73Interface(TCPClientInterface):
DEFAULT_CONTROL_PORT = 8073
DEFAULT_MTU_OVERHEAD = 15
DEFAULT_BITRATE = 600
+ DEFAULT_SHORT_MTU = 170
CONTROL_RECONNECT_WAIT = 5
CONTROL_CONNECT_TIMEOUT = 5
+ MODEM_OFDM = 0
+ MODEM_MFSK = 1
+ MODEM_ROBUST = 2
+
+ OFDM_MODULATIONS = ["BPSK", "QPSK", "8PSK", "QAM16",
+ "QAM64", "QAM256", "QAM1024", "QAM4096"]
+ OFDM_CODE_RATES = ["1/2", "2/3", "3/4", "5/6", "1/4", "1/2x2", "1/4x2"]
+
+ ROBUST_SHORT_OFFSET = 5
+ ROBUST_MODE_MAX = 9
+ ROBUST_BPS = [1150, 585, 296, 296, 149, 732, 378, 194, 197, 99]
+
+ PKT_LINKREQUEST = 0x02
+ CTX_LRRTT = 0xFE
+ CTX_LRPROOF = 0xFF
+
def __init__(self, owner, configuration):
c = Interface.get_config_obj(configuration)
@@ -47,6 +68,17 @@ class Modem73Interface(TCPClientInterface):
mtu_overhead = int(c["mtu_overhead"]) if "mtu_overhead" in c else self.DEFAULT_MTU_OVERHEAD
bitrate = int(c["bitrate"]) if "bitrate" in c else self.DEFAULT_BITRATE
auto_frag = c.as_bool("auto_fragmentation") if "auto_fragmentation" in c else True
+ short_frames = str(c["short_frames"]).lower() if "short_frames" in c else "off"
+ short_mtu = int(c["short_mtu"]) if "short_mtu" in c else self.DEFAULT_SHORT_MTU
+ handshake_x2 = c.as_bool("handshake_x2") if "handshake_x2" in c else False
+ auto_bitrate = c.as_bool("auto_bitrate") if "auto_bitrate" in c else True
+
+ if short_frames not in ("off", "auto", "always"):
+ RNS.log(
+ f"Modem73Interface: invalid short_frames value \"{short_frames}\", using \"off\"",
+ RNS.LOG_WARNING,
+ )
+ short_frames = "off"
self.control_host = control_host
self.control_port = control_port
@@ -59,6 +91,14 @@ class Modem73Interface(TCPClientInterface):
self._control_thread = None
self._control_stop = False
+ self._short_policy = short_frames
+ self._short_mtu = short_mtu
+ self._short_oper_mode = None
+ self._short_tx_count = 0
+ self._always_applied = False
+ self._handshake_x2 = handshake_x2
+ self._auto_bitrate = auto_bitrate
+
initial_mtu = self._query_initial_mtu()
if initial_mtu is None:
@@ -230,6 +270,7 @@ class Modem73Interface(TCPClientInterface):
except Exception:
pass
self._control_socket = None
+ self._always_applied = False
if self._control_stop or self.detached:
break
@@ -254,6 +295,143 @@ class Modem73Interface(TCPClientInterface):
if "payload_size" in cfg:
self._apply_payload_size(cfg["payload_size"])
+ self._update_short_mode(cfg)
+
+ if self._short_policy == "always" and not self._always_applied:
+ self._apply_always_short(cfg)
+
+ ### SHORT FRAME HANDLING
+
+ def _update_short_mode(self, cfg):
+ modem_type = cfg.get("modem_type")
+ override = None
+
+ if modem_type == self.MODEM_ROBUST:
+ rm = cfg.get("robust_mode")
+ if self._auto_bitrate and rm is not None and 0 <= rm <= self.ROBUST_MODE_MAX:
+ new_bitrate = self.ROBUST_BPS[rm]
+ if new_bitrate != self.bitrate:
+ RNS.log(
+ f"Modem73Interface[{self.name}]: bitrate {self.bitrate} -> "
+ f"{new_bitrate} (TNC mode change)",
+ RNS.LOG_INFO,
+ )
+ self.bitrate = new_bitrate
+ if rm is not None and rm < self.ROBUST_SHORT_OFFSET:
+ override = rm + self.ROBUST_SHORT_OFFSET
+ self._short_mtu = min(self._short_mtu, self.DEFAULT_SHORT_MTU)
+
+ elif modem_type == self.MODEM_OFDM:
+ if not cfg.get("short_frame", False):
+ try:
+ mod = self.OFDM_MODULATIONS.index(cfg.get("modulation"))
+ rate = self.OFDM_CODE_RATES.index(cfg.get("code_rate"))
+ override = (mod << 4) | (rate << 1)
+ except (ValueError, TypeError):
+ override = None
+
+ if override != self._short_oper_mode:
+ self._short_oper_mode = override
+ if self._short_policy == "auto":
+ if override is not None:
+ RNS.log(
+ f"Modem73Interface[{self.name}]: short-frame bypass active "
+ f"(mode override {override}, threshold {self._short_mtu} bytes)",
+ RNS.LOG_INFO,
+ )
+ else:
+ RNS.log(
+ f"Modem73Interface[{self.name}]: short-frame bypass inactive "
+ f"for current TNC mode",
+ RNS.LOG_INFO,
+ )
+
+ def _apply_always_short(self, cfg):
+ modem_type = cfg.get("modem_type")
+ msg = None
+
+ if modem_type == self.MODEM_ROBUST:
+ rm = cfg.get("robust_mode")
+ if rm is not None and rm < self.ROBUST_SHORT_OFFSET:
+ msg = {"cmd": "set_config", "robust_mode": rm + self.ROBUST_SHORT_OFFSET}
+ elif modem_type == self.MODEM_OFDM:
+ if not cfg.get("short_frame", False):
+ msg = {"cmd": "set_config", "short_frame": True}
+
+ if msg is None:
+ self._always_applied = True
+ return
+
+ with self._control_lock:
+ sock = self._control_socket
+ if sock is None:
+ return
+ try:
+ self._send_cmd(sock, msg)
+ self._always_applied = True
+ RNS.log(
+ f"Modem73Interface[{self.name}]: switched TNC to short-frame "
+ f"mode ({msg})",
+ RNS.LOG_INFO,
+ )
+ except Exception as e:
+ RNS.log(
+ f"Modem73Interface[{self.name}]: failed to apply "
+ f"short_frames=always: {e}",
+ RNS.LOG_WARNING,
+ )
+
+ def _is_handshake(self, data):
+ if len(data) < 19:
+ return False
+ flags = data[0]
+ if (flags & 0x03) == self.PKT_LINKREQUEST:
+ return True
+ ctx_offset = 34 if (flags >> 6) & 0x01 else 18
+ if len(data) <= ctx_offset:
+ return False
+ return data[ctx_offset] in (self.CTX_LRRTT, self.CTX_LRPROOF)
+
+ def process_outgoing(self, data):
+ copies = 1
+ if (self._handshake_x2
+ and getattr(self, "ifac_identity", None) is None
+ and self._is_handshake(data)):
+ copies = 2
+
+ for _ in range(copies):
+ if (self._short_policy == "auto"
+ and self.online
+ and self._short_oper_mode is not None
+ and len(data) <= self._short_mtu
+ and self._send_short_frame(data)):
+ continue
+ super().process_outgoing(data)
+
+ def _send_short_frame(self, data):
+ msg = {
+ "cmd": "tx",
+ "data": base64.b64encode(bytes(data)).decode("ascii"),
+ "oper_mode": int(self._short_oper_mode),
+ }
+ with self._control_lock:
+ sock = self._control_socket
+ if sock is None:
+ return False
+ try:
+ self._send_cmd(sock, msg)
+ except Exception as e:
+ RNS.log(
+ f"Modem73Interface[{self.name}]: short-frame tx failed, "
+ f"falling back to KISS: {e}",
+ RNS.LOG_WARNING,
+ )
+ return False
+
+ self.txb += len(data)
+ self._short_tx_count += 1
+ return True
+

diff --git a/README.md b/README.md
index 60cc349..8cb1d8e 100644
--- a/README.md
+++ b/README.md
@@ -13,3 +13,9 @@ control_host = 127.0.0.1
control_port = 8073
```
where the target host, port, and control host and port are pointing to your runnning modem73 instance
+
+# Tips
+- All peers using modem73 should have eachother's announces in their path table. It's best to Announce each time rnsd or other Reticulum programs are spun up when using modem73 as an Interface.
+- It's best to use modes that don't require fragmentation when conditions allow for it.
+- For Transports that connect modem73 to a faster / noisier medium like TCP/IP (such as the public Reticulum network), they should always be configured to `boundary` mode (See: https://reticulum.network/manual/interfaces.html#interfaces-modes)
+- Make sure that CSMA (collision avoidance) is properly configured. Ensure that the level threshold is properly set to avoid collisions

Served by rngit 1.5.0 - Generated in 0.03s